﻿using System;
using System.Net.Sockets;
using System.Threading;
using UtilZ.Dotnet.Ex.Base;
using UtilZ.Dotnet.Ex.Communication.Net.Base;
using UtilZ.Dotnet.Ex.ZLog;

namespace UtilZ.Dotnet.Ex.Communication.Net.Tcp
{
    /// <summary>
    /// Tcp监听类
    /// </summary>
    public class NetTcpListener : IDisposable
    {
        private readonly NetTcpListenerInitPara _initPara;
        /// <summary>
        /// 初始化参数
        /// </summary>
        public NetTcpListenerInitPara InitPara
        {
            get { return this._initPara; }
        }


        /// <summary>
        /// 监听Socket
        /// </summary>
        private Socket _listenSocket = null;
        /// <summary>
        /// 监听Socket
        /// </summary>
        public Socket ListenSocket
        {
            get { return this._listenSocket; }
        }

        private SocketAsyncEventArgs _asyncAcceptArgs = null;

        /// <summary>
        /// 监听线程
        /// </summary>
        private ThreadEx _tcpListenThread = null;

        /// <summary>
        /// 监听到客户端连接后输出回调
        /// </summary>
        private readonly Action<AcceptSocketArgs> _acceptSocket;
        private void OnRaiseAcceptSocket(Socket client)
        {
            var args = new AcceptSocketArgs(this, this._initPara.ListenEndPoint, client, this._semaphoreMaxClients != null);
            this._acceptSocket(args);
        }


        private bool _accepting = false;
        /// <summary>
        /// 指示NetTcpListener当前状态,是否处理Accept中
        /// </summary>
        public bool Accepting
        {
            get { return _accepting; }
        }


        /// <summary>
        /// 控制tcp客户端连接数量的信号量
        /// </summary>
        private Semaphore _semaphoreMaxClients = null;


        private bool _disposed = false;




        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="acceptSocket">监听到客户端连接后输出回调</param>
        /// <param name="initPara">初始化参数</param>
        /// <exception cref="ArgumentNullException">acceptSocket或listenEndPoint为null将抛出异常</exception>
        /// <exception cref="ArgumentOutOfRangeException">backlog小于等于1抛出异常</exception>
        public NetTcpListener(Action<AcceptSocketArgs> acceptSocket, NetTcpListenerInitPara initPara)
        {
            if (acceptSocket == null)
            {
                throw new ArgumentNullException(nameof(acceptSocket));
            }

            if (initPara == null)
            {
                throw new ArgumentNullException(nameof(initPara));
            }

            this._acceptSocket = acceptSocket;
            this._initPara = initPara;
        }





        /// <summary>
        /// 监听线程方法
        /// </summary>
        /// <param name="threadPara">线程参数</param>
        private void TcpListenThreadMethod(ThreadPara threadPara)
        {
            try
            {
                ZLoger.Info($"\"{Thread.CurrentThread.Name}\"已启动...");
                Socket listenSocket = this._listenSocket;
                Socket client;


                while (!threadPara.Token.IsCancellationRequested && this._accepting)
                {
                    try
                    {
                        if (this._disposed)
                        {
                            break;
                        }

                        if (this.CheckConnectionCount())
                        {
                            client = listenSocket.Accept();
                            this.OnRaiseAcceptSocket(client);
                        }
                        else
                        {
                            break;
                        }
                    }
                    catch (SocketException sex)
                    {
                        if (!NetHelper.SocketErrorIsDisconnect(sex.SocketErrorCode))
                        {
                            ZLoger.Error(sex);
                        }
                    }
                    catch (ObjectDisposedException)
                    {
                        break;
                    }
                    catch (Exception exi)
                    {
                        ZLoger.Error(exi);
                    }
                }
            }
            catch (Exception ex)
            {
                ZLoger.Error(ex, $"\"{Thread.CurrentThread.Name}\"异常");
            }
        }

        private bool CheckConnectionCount()
        {
            bool result = false;
            try
            {
                if (this._semaphoreMaxClients == null)
                {
                    //等于null表示无限制
                    if (this._accepting)
                    {
                        result = true;
                    }
                }
                else
                {
                    while (this._accepting)
                    {
                        try
                        {
                            if (this._semaphoreMaxClients.WaitOne(this._initPara.ConnectionLimitIntervalMilliseconds))
                            {
                                if (this._accepting)
                                {
                                    //ZLoger.Debug("this._semaphoreMaxClients.WaitOne(this._initPara.ConnectionLimitIntervalMilliseconds)");
                                    result = true;
                                }
                                break;
                            }
                        }
                        catch (ObjectDisposedException)
                        {
                            break;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                ZLoger.Error(ex);
            }

            return result;
        }

        private void AcceptAsync()
        {
            if (this._disposed)
            {
                return;
            }

            Socket listenSocket = this._listenSocket;
            if (listenSocket == null)
            {
                return;
            }

            try
            {
                if (this.CheckConnectionCount())
                {
                    if (!listenSocket.AcceptAsync(this._asyncAcceptArgs))
                    {
                        this.ProcessAcceptAsyncSocket(this._asyncAcceptArgs);
                    }
                }
            }
            catch (ObjectDisposedException)
            {

            }
            catch (Exception ex)
            {
                ZLoger.Error(ex);
            }
        }

        private void AcceptAsync_Completed(object sender, SocketAsyncEventArgs e)
        {
            this.ProcessAcceptAsyncSocket(e);
        }

        private void ProcessAcceptAsyncSocket(SocketAsyncEventArgs e)
        {
            try
            {
                if (e.SocketError == SocketError.Success)
                {
                    this.OnRaiseAcceptSocket(e.AcceptSocket);
                    e.AcceptSocket = null;
                }

                this.AcceptAsync();
            }
            catch (ObjectDisposedException)
            {

            }
            catch (Exception ex)
            {
                ZLoger.Error(ex);
            }
        }



        private Socket CreateListenSocket()
        {
            NetTcpListenerInitPara initPara = this._initPara;

            var listenEndPoint = initPara.ListenEndPoint;
            if (listenEndPoint == null)
            {
                throw new ArgumentNullException(nameof(listenEndPoint));
            }

            var backlog = initPara.Backlog;
            if (backlog < 1)
            {
                throw new ArgumentOutOfRangeException(nameof(backlog), "不能小于1");
            }

            var listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {
                listenSocket.Bind(listenEndPoint);
            }
            catch (SocketException sex)
            {
                listenSocket.Dispose();
#if CORE
                throw new SocketException(sex.ErrorCode, $"{sex.Message},{NetHelper.GetIPPortString(listenEndPoint)}");
#else
                throw new Exception($"{sex.Message},{NetHelper.GetIPPortString(listenEndPoint)}",sex);
#endif
            }
            catch (Exception ex)
            {
                listenSocket.Dispose();
                throw new Exception($"{ex.Message},{NetHelper.GetIPPortString(listenEndPoint)}", ex);
            }
            listenSocket.Listen(backlog);
            return listenSocket;
        }

        /// <summary>
        /// 开始监听
        /// </summary>
        public void Start()
        {
            lock (this)
            {
                if (this._accepting)
                {
                    //"已开始监听"
                    return;
                }
                this._accepting = true;
            }

            this._listenSocket = this.CreateListenSocket();

            var maxConnections = this._initPara.MaxConnections;
            if (maxConnections > 0)
            {
                this._semaphoreMaxClients = new Semaphore(maxConnections, maxConnections);
            }
            else
            {
                this._semaphoreMaxClients = null;
            }

            switch (this._initPara.AcceptMode)
            {
                case ThreadMode.Sync:
                case ThreadMode.Spin:
                    if (this._tcpListenThread == null)
                    {
                        string syncThreadName = this._initPara.SyncThreadName;
                        if (string.IsNullOrWhiteSpace(syncThreadName))
                        {
                            syncThreadName = $"tcp监听线程{this.GetHashCode()}";
                        }
                        this._tcpListenThread = new ThreadEx(this.TcpListenThreadMethod, syncThreadName, true);
                    }
                    this._tcpListenThread.Start();
                    break;
                case ThreadMode.Async:
                    if (this._asyncAcceptArgs == null)
                    {
                        this._asyncAcceptArgs = new SocketAsyncEventArgs();
                        this._asyncAcceptArgs.Completed += AcceptAsync_Completed;
                    }
                    this.AcceptAsync();
                    break;
                default:
                    throw new ArgumentException($"无效的线程模式");
            }
        }

        /// <summary>
        /// 停止监听
        /// </summary>
        public void Stop()
        {
            try
            {
                lock (this)
                {
                    if (!this._accepting)
                    {
                        return;
                    }
                    this._accepting = false;
                }

                switch (this._initPara.AcceptMode)
                {
                    case ThreadMode.Sync:
                    case ThreadMode.Spin:
                        this._tcpListenThread.Stop();
                        break;
                    case ThreadMode.Async:
                        break;
                    default:
                        throw new ArgumentException($"无效的线程模式");
                }

                Socket listenSocket = this._listenSocket;
                if (listenSocket != null)
                {
                    listenSocket.Dispose();
                    this._listenSocket = null;
                }

                if (this._semaphoreMaxClients != null)
                {
                    this._semaphoreMaxClients.Release();
                    this._semaphoreMaxClients.Dispose();
                    this._semaphoreMaxClients = null;
                }
            }
            catch (Exception ex)
            {
                ZLoger.Error(ex);
            }
        }

        /// <summary>
        /// 当连接断开后,可用连接数+1
        /// </summary>
        public void IncrementConnections()
        {
            if (this._semaphoreMaxClients != null)
            {
                //不等于null表示有连接数据限制
                try
                {
                    this._semaphoreMaxClients.Release();
                    //ZLoger.Debug("this._semaphoreMaxClients.Release()");
                }
                catch (SemaphoreFullException)
                {
                    //Adding the specified count to the semaphore would cause it to exceed its maximum count
                }
                catch (ObjectDisposedException)
                {

                }
            }
            else
            {
                //等于null表示无连接数据限制
            }
        }


        /// <summary>
        /// Dispose
        /// </summary>
        public void Dispose()
        {
            try
            {
                if (this._disposed)
                {
                    return;
                }
                this._disposed = true;

                this.Stop();
            }
            catch (Exception ex)
            {
                ZLoger.Error(ex, "释放资源异常");
            }
        }

    }

}
